package com.tianji.remark.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.api.dto.remark.LikedTimesDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;
import static com.tianji.remark.constans.RedisConstants.LIKES_BIZ_KEY_PREFIX;
import static com.tianji.remark.constans.RedisConstants.LIKES_TIME_KEY_PREFIX;

@Service
@RequiredArgsConstructor
public class LikedRecordServiceRedisImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {
    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    /**
     * 1.点赞或取消点赞方法
     *
     * @param likeRecordFormDTO 点赞业务数据DTO
     */
    @Override
    public void likeOrUnLike(LikeRecordFormDTO likeRecordFormDTO) {
        //1、基于前端参数，判断点赞还是取消点赞，根据判断结果，执行点赞、取消点赞，返回是否成功
        Boolean result = likeRecordFormDTO.getLiked() ? like(likeRecordFormDTO) : unLike(likeRecordFormDTO);
        // 2、判断是否执行成功，如果失败，直接结束
        if (!result) {
            return;
        }
        // 3、如果执行成功，统计点赞数
        Long likeTimes = redisTemplate.opsForSet().size(LIKES_BIZ_KEY_PREFIX + likeRecordFormDTO.getBizId());
        // 4、缓存点赞总数到redis zset结构中
        if (likeTimes != null) {
            redisTemplate.opsForZSet().add(LIKES_TIME_KEY_PREFIX + likeRecordFormDTO.getBizType(),
                    likeRecordFormDTO.getBizId().toString(), likeTimes);
        }
    }

    /**
     * 取消点赞
     *
     * @param likeRecordFormDTO
     * @return
     */
    private Boolean unLike(LikeRecordFormDTO likeRecordFormDTO) {
        // 1、组装大key
        String key = LIKES_BIZ_KEY_PREFIX + likeRecordFormDTO.getBizId();
        // 2、执行set结构的srem命令，从点赞业务ID的set集合中删除点赞人id
        Long result = redisTemplate.opsForSet().remove(key, UserContext.getUser().toString());
        // 3、返回取消点赞是否成功
        return ObjectUtils.isNotEmpty(result) && result > 0;

    }

    /**
     * 点赞
     *
     * @param likeRecordFormDTO
     * @return
     */
    private Boolean like(LikeRecordFormDTO likeRecordFormDTO) {
        // 1、组装大key
        String key = LIKES_BIZ_KEY_PREFIX + likeRecordFormDTO.getBizId();
        // 2、执行set结构的srem命令，从点赞业务ID的set集合中删除点赞人id
        Long result = redisTemplate.opsForSet().add(key, UserContext.getUser().toString());
        // 3、返回取消点赞是否成功
        return ObjectUtils.isNotEmpty(result) && result > 0;
    }


    /**
     * 2.根据业务ID查询该用户对业务的点赞状态
     *
     * @param bizIds 回答或评论的id集合
     */
    @Override
    public Set<Long> isLiked(List<Long> bizIds) {
        //1.获取当前登录人的id
        Long userId = UserContext.getUser();
        //2.批量查询点赞状态，封装到set集合中，返回
        HashSet<Long> ids = new HashSet<>();
        for (Long bizId : bizIds) {
            Boolean exists = redisTemplate.opsForSet().isMember(LIKES_BIZ_KEY_PREFIX + bizId, userId);
            if (BooleanUtils.isTrue(exists)) {
                ids.add(bizId);
            }
        }
        return ids;
    }


    /**
     * 3、定时读取zset中的点赞数并发送消息至业务方 - 更新点赞数（Day6 -随堂3）
     *
     * @param bizType
     * @param maxLikedTimes
     */
    @Override
    public void readLikedTimesAndSendMessage(String bizType, Integer maxLikedTimes) {
//        // 1、批量读取redis zset中的缓存点赞总数 (元组)
//        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().popMin(LIKES_TIME_KEY_PREFIX + bizType, maxLikedTimes);
//        // 2、数据转换 ->  List<LikedTimesDTO>
//        while (ObjectUtils.isNotEmpty(typedTuples)) {
//            List<LikedTimesDTO> message = new ArrayList<>(typedTuples.size());
//            for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
//                String bizId = typedTuple.getValue();
//                Double likeTimes = typedTuple.getScore();
//                if (bizId == null || likeTimes == null) {
//                    continue;
//                }
//                message.add(LikedTimesDTO.of(Long.valueOf(bizId), likeTimes.intValue()));
//            }
//            if (ObjectUtils.isNotEmpty(message)) {
//                //3.发送MQ消息
//                mqHelper.send(LIKE_RECORD_EXCHANGE,
//                        StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, bizType),
//                        message);
//            }
//        }
        // 1、批量读取redis zset中的缓存点赞总数 (元组)
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().popMin(LIKES_TIME_KEY_PREFIX + bizType, maxLikedTimes);

        // 2、数据转换 ->  List<LikedTimesDTO>
        while (ObjectUtils.isNotEmpty(typedTuples)) {
            List<LikedTimesDTO> messages = new ArrayList<>(typedTuples.size());
            for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
                String bizId = typedTuple.getValue();
                Double likeTimes = typedTuple.getScore();
                if(bizId == null || likeTimes == null){
                    continue;
                }
                messages.add(LikedTimesDTO.of(Long.valueOf(bizId),likeTimes.intValue()));
            }

            if(ObjectUtils.isNotEmpty(messages)){
                // 3、发送MQ消息
                mqHelper.send(LIKE_RECORD_EXCHANGE,
                        StringUtils.format(LIKED_TIMES_KEY_TEMPLATE,bizType),
                        messages);
            }

            // 4、继续查询下一轮
            typedTuples = redisTemplate.opsForZSet().popMin(LIKES_TIME_KEY_PREFIX + bizType, maxLikedTimes);
        }

    }
}
